/*
 * Copyright (c) 2023 Shenzhen Kaihong Digital Industry Development Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "se_napi_oma_seservice.h"

#include <string>
#include <uv.h>
#include "se_napi_oma_common.h"
#include "se_napi_oma_reader.h"

namespace OHOS {
namespace SE {
namespace OMAPI {

thread_local napi_ref NapiSEOmaSEService::consRef_ = nullptr;
std::map<std::string, std::vector<napi_ref>> g_connectCallbacks = {};
napi_env g_env = nullptr;
static std::mutex g_mutex {};
static const int32_t DEFAULT_REF_COUNT = 1;

napi_status NapiSEOmaSEService::CreateJSReaderList(napi_env env, std::vector<std::shared_ptr<Reader>> &readerList,
    napi_value &jsReaderList)
{
    if (readerList.empty()) {
        ErrorLog("CreateJSReaderList readerList empty");
        return napi_invalid_arg;
    }
    // create return readerlist
    napi_create_array_with_length(env, readerList.size(), &jsReaderList);
    for (uint32_t i = 0; i < readerList.size(); i++) {
        NapiSEOmaReader *napiReader = new NapiSEOmaReader();
        if (napiReader == nullptr) {
            ErrorLog("GetReaders napiReader failed!");
            return napi_invalid_arg;
        }
        // new instance of JS object
        napiReader->reader_ = readerList[i];
        napi_value reader = nullptr;
        napi_value constructor = nullptr;
        napi_get_reference_value(env, NapiSEOmaReader::consRef_, &constructor);
        napi_new_instance(env, constructor, 0, nullptr, &reader);
        napi_status status = napi_wrap(
            env, reader, napiReader,
            [](napi_env env, void *data, void *hint) {
                ErrorLog("NapiSEOmaSEService::CreateJSReaderList delete JsReader!");
                if (data) {
                    NapiSEOmaReader* service = (NapiSEOmaReader*)data;
                    if (service) {
                        delete service;
                        service = nullptr;
                    }
                }
            },
            nullptr, nullptr);
        if (status != napi_ok) {
            if (napiReader != nullptr) {
                delete napiReader;
                napiReader = nullptr;
            }
            ErrorLog("GetReaders, napi_wrap failed, object is null. status = %{public}d", status);
            return status;
        }
        NAPI_SE_CALL_RETURN(napi_set_element(env, jsReaderList, i, reader));
    }
    DebugLog("SeService::GetReaders: %{public}lu", readerList.size());
    return napi_ok;
}

napi_value JSOmaSEServiceClassConstructor(napi_env env, napi_callback_info info)
{
    DebugLog("Enter function JSOmaSEServiceClassConstructor");
    napi_value thisVar = nullptr;
    size_t argc = ARGV_NUM_2;
    napi_value argv[ARGV_NUM_2] = {nullptr};
    napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr);
    // TO-DO: get paras
    NapiSEOmaSEService* omaJSClass = new NapiSEOmaSEService();

    napi_status status = napi_wrap(
        env,
        thisVar,
        omaJSClass,
        [](napi_env env, void *data, void *hint) {
            NapiSEOmaSEService *service = (NapiSEOmaSEService *)data;
            if (service) {
                delete service;
                service = nullptr;
            }
        },
        nullptr,
        nullptr);
    if (status != napi_ok) {
        if (omaJSClass != nullptr) {
            delete omaJSClass;
            omaJSClass = nullptr;
        }
        ErrorLog("napi_wrap failed, object is null. status = %{public}d", status);
        return NapiSEOmaCommon::CreateUndefined(env);
    }
    return thisVar;
}

void NapiSEOmaSEService::DefineSEOmaSEServiceJsClass(napi_env env, napi_value exports)
{
    DebugLog("Enter function DefineSEOmaSEServiceJsClass");
    napi_property_descriptor desc[] = {
        DECLARE_NAPI_FUNCTION("getReaders", NapiSEOmaSEService::GetReaders),
        DECLARE_NAPI_FUNCTION("isConnected", NapiSEOmaSEService::IsConnected),
        DECLARE_NAPI_FUNCTION("shutdown", NapiSEOmaSEService::Shutdown),
        DECLARE_NAPI_FUNCTION("getVersion", NapiSEOmaSEService::GetVersion),
    };
    // define JS class SEOmaSEService, JS_Constructor is the callback function
    napi_value constructor = nullptr;
    napi_define_class(env, "SEOmaSEService", NAPI_AUTO_LENGTH, JSOmaSEServiceClassConstructor,
        nullptr, sizeof(desc)/sizeof(desc[0]), desc, &constructor);
    napi_create_reference(env, constructor, DEFAULT_REF_COUNT, &consRef_);
}

napi_status NapiSEOmaSEService::CreateSEServiceInstance(napi_env env,
                                                        napi_callback_info info,
                                                        const std::string &className,
                                                        napi_value &outResult)
{
    DebugLog("Enter function NapiSEOmaSEService::CreateSEServiceInstance:");
    size_t expectedArgsCount = ARGV_NUM_2;
    size_t argc = expectedArgsCount;
    napi_value argv[ARGV_NUM_2] = {0};
    NAPI_SE_CALL_RETURN(napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
    NapiSEOmaCommon::CheckArgCountAndThrow(env, argc, expectedArgsCount);
    if (!NapiSEOmaCommon::IsTargetValueType(env, argv[ARGV_INDEX_0], napi_string)) {
        ErrorLog("parameter type error");
        NapiSEOmaCommon::ThrowParameterError(env);
    }

    napi_value constructor = nullptr;
    NAPI_SE_CALL_RETURN(napi_get_reference_value(env, consRef_, &constructor));
    NAPI_SE_CALL_RETURN(napi_new_instance(env, constructor, argc, argv, &outResult));

    std::unique_ptr<ServiceContext> asyncContext = std::make_unique<ServiceContext>(env);
    napi_create_reference(env, argv[ARGV_INDEX_1], DEFAULT_REF_COUNT, &(asyncContext->callbackRef));
    NapiSEOmaSEService *napiService = nullptr;
    NAPI_SE_CALL_RETURN(napi_unwrap(env, outResult, reinterpret_cast<void **>(&napiService)));
    napiService->seService = std::make_shared<SEService>();
    if (napiService->seService == nullptr) {
        ErrorLog("GetService napiService->seService nullptr!");
        return napi_invalid_arg;
    }
    asyncContext->napiService = napiService;
    NapiSEOmaCommon::HandleAsyncWork(env, asyncContext.release(), "NewSEService",
        NativeNewSEService, NativeNewSEServiceCallBack);
    DebugLog("NapiSEOmaSEService::CreateSEServiceInstance done.");
    return napi_ok;
}

void NapiSEOmaSEService::SeServiceDeathCallback()
{
    InfoLog("SeServiceDeathCallback");
    std::lock_guard<std::mutex> lock(g_mutex);
    if (g_env == nullptr || g_connectCallbacks.empty()) {
        ErrorLog("g_env is nullptr or g_connectCallbacks is empty");
        return;
    }

    // callback must run in main thread.
    DebugLog("Notify the application that the se service is dead.");
    /* 1) get loop about napi_env */
    uv_loop_s *loop = nullptr;
    napi_get_uv_event_loop(g_env, &loop);
    if (loop == nullptr) {
        ErrorLog("loop is null");
        return;
    }
    uv_work_t *work = new uv_work_t;

    /* 2) commit work request */
    uv_queue_work_internal(
        loop,
        work,
        [](uv_work_t *work) { DebugLog("uv_queue_work: exec"); },
        [](uv_work_t *work, int status) {
            DebugLog("uv_queue_work: complete.");
            std::string uniqueTag = CommonUtils::GetUniqueTag();
            napi_handle_scope scope = nullptr;
            napi_open_handle_scope(g_env, &scope);
            for (napi_ref connectCallback : g_connectCallbacks[uniqueTag]) {
                /* 3) get callback function from napi_ref */
                napi_value cb = 0;
                napi_get_reference_value(g_env, connectCallback, &cb);

                /* 4) get input parameters of function */
                napi_value retValue = 0;
                napi_create_int64(g_env, ServiceState::DISCONNECTED, &retValue);
                napi_value args[1] = {retValue};

                /* 5) call funtion */
                napi_value cb_result;
                napi_call_function(g_env, nullptr, cb, 1, args, &cb_result);

                /* 6) Obtain the function execution result */
                uint32_t value;
                napi_get_value_uint32(g_env, cb_result, &value);
                DebugLog("uv_queue_work: status(%d).", value);
            }
            napi_close_handle_scope(g_env, scope);
            g_connectCallbacks[uniqueTag].clear();
            delete work;
            work = nullptr;
        },
        "SeServiceDeath");
}

void NapiSEOmaSEService::NativeNewSEService(napi_env env, void *data)
{
    DebugLog("NapiSEOmaSEService::NativeNewSEService start.");
    if (data == nullptr) {
        ErrorLog("NapiSEOmaSEService::NativeNewSEService data is nullptr");
        return;
    }
    ServiceContext *asyncContext = static_cast<ServiceContext *>(data);
    asyncContext->serviceState = ServiceState::DISCONNECTED;
    bool initStatus = false;
    int statusCode = asyncContext->napiService->seService->InitSEService(initStatus, SeServiceDeathCallback);
    asyncContext->errorCode = statusCode;
    if (!initStatus || statusCode != ErrorCode::SE_SUCCESS) {
        ErrorLog("NapiSEOmaSEService::NewSEService InitSEService falied");
        asyncContext->resolved = false;
        return;
    }
    asyncContext->serviceState = ServiceState::CONNECTED;
    asyncContext->resolved = true;
    DebugLog("NapiSEOmaSEService::NativeNewSEService done.");
}

void NapiSEOmaSEService::NativeNewSEServiceCallBack(napi_env env, napi_status status, void *data)
{
    DebugLog("NapiSEOmaSEService::NativeNewSEServiceCallBack start.");
    if (data == nullptr) {
        ErrorLog("NapiSEOmaSEService::NativeNewSEServiceCallBack data is nullptr");
        return;
    }
    ServiceContext *asyncContext = static_cast<ServiceContext *>(data);
    if (status == napi_ok && asyncContext->resolved && asyncContext->errorCode == ErrorCode::SE_SUCCESS) {
        napi_value napiServiceState = nullptr;
        napi_create_int32(env, asyncContext->serviceState, &napiServiceState);
        if (asyncContext->callbackRef != nullptr) {
            InfoLog("DoAsyncCallbackOrPromise for callback");
            constexpr int RESULT_PARAMS_NUM = 1;
            napi_value result = nullptr;
            napi_value callbackFunc = nullptr;
            NAPI_CALL_RETURN_VOID(env, napi_get_reference_value(env, asyncContext->callbackRef, &callbackFunc));
            std::string uniqueTag = CommonUtils::GetUniqueTag();
            {
                std::lock_guard<std::mutex> lock(g_mutex);
                g_connectCallbacks[uniqueTag].emplace_back(asyncContext->callbackRef);
                g_env = env;
            }
            napi_value callbackValues[RESULT_PARAMS_NUM];
            callbackValues[0] = napiServiceState;
            NAPI_CALL_RETURN_VOID(
                env, napi_call_function(env, nullptr, callbackFunc, RESULT_PARAMS_NUM, callbackValues, &result));
        }
    } else {
        int errCode = BuildOutputErrorCode(asyncContext->errorCode);
        std::string msg = NapiSEOmaCommon::BuildErrorMessage(errCode, "NativeOpenBasicChannelCallBack", "", "", "");
        ThrowAsyncError(env, asyncContext, errCode, msg, "");
        ErrorLog("NapiSEOmaSEService::NativeNewSEServiceCallBack error handling done.");
        return;
    }
    napi_delete_async_work(env, asyncContext->work);
    DebugLog("NapiSEOmaSEService::NativeNewSEServiceCallBack done.");
}

void NapiSEOmaSEService::NativeCreateService(napi_env env, void *data)
{
    DebugLog("NapiSEOmaSEService::NativeCreateService start.");
    ServiceContext *asyncContext = static_cast<ServiceContext *>(data);
    asyncContext->serviceState = ServiceState::DISCONNECTED;
    bool initStatus = false;
    int statusCode = asyncContext->service->InitSEService(initStatus, SeServiceDeathCallback);
    asyncContext->errorCode = statusCode;
    if (!initStatus || statusCode != ErrorCode::SE_SUCCESS) {
        ErrorLog("NapiSEOmaSEService::CreateService InitSEService falied");
        asyncContext->resolved = false;
        return;
    }
    asyncContext->serviceState = ServiceState::CONNECTED;
    asyncContext->resolved = true;
    DebugLog("NapiSEOmaSEService::NativeCreateService done.");
}

void NapiSEOmaSEService::NativeCreateServiceCallBack(napi_env env, napi_status status, void *data)
{
    DebugLog("NapiSEOmaSEService::NativeCreateServiceCallBack start.");
    if (data == nullptr) {
        ErrorLog("NapiSEOmaSEService::NativeCreateServiceCallBack data is nullptr");
        return;
    }
    ServiceContext *asyncContext = static_cast<ServiceContext *>(data);
    if (status == napi_ok && asyncContext->resolved && asyncContext->errorCode == ErrorCode::SE_SUCCESS) {
        NapiSEOmaSEService *napiService = new NapiSEOmaSEService();
        napiService->seService = asyncContext->service;
        if (napiService->seService == nullptr) {
            WarnLog("NapiSEOmaSEService::NativeCreateServiceCallBack error, seService is null");
        }
        napi_value seService = nullptr;
        napi_value constructor = nullptr;
        napi_get_reference_value(env, NapiSEOmaSEService::consRef_, &constructor);
        napi_new_instance(env, constructor, 0, nullptr, &seService);
        napi_status status = napi_wrap(
            env, seService, napiService,
            [](napi_env env, void *data, void *hint) {
                ErrorLog("NapiSEOmaSEService::NativeCreateServiceCallBack delete JsSeService!");
                if (data) {
                    NapiSEOmaSEService* jsSeService = static_cast<NapiSEOmaSEService *>(data);
                    if (jsSeService) {
                        delete jsSeService;
                        jsSeService = nullptr;
                    }
                }
            },
            nullptr, nullptr);
        if (status != napi_ok) {
            if (napiService != nullptr) {
                delete napiService;
                napiService = nullptr;
            }
            WarnLog("napi_wrap failed, object is null. status = %{public}d", status);
        }
        DoAsyncCallbackOrPromise(env, asyncContext, seService);
    } else {
        int errCode = BuildOutputErrorCode(asyncContext->errorCode);
        std::string msg = NapiSEOmaCommon::BuildErrorMessage(errCode, "NativeOpenBasicChannelCallBack", "", "", "");
        ThrowAsyncError(env, asyncContext, errCode, msg, "");
        ErrorLog("NapiSEOmaSEService::NativeNewSEServiceCallBack error handling done.");
    }
    DebugLog("NapiSEOmaSEService::NativeNewSEServiceCallBack done.");
}

napi_value NapiSEOmaSEService::NewSEService(napi_env env, napi_callback_info info)
{
    napi_value result = nullptr;
    DebugLog("Enter function NapiSEOmaSEService::NewSEService:");
    napi_status status = CreateSEServiceInstance(env, info, "SEOmaSEService", result);
    if (status != napi_ok) {
        ErrorLog("NapiSEOmaSEService::NewSEService falied");
        return NapiSEOmaCommon::CreateUndefined(env);
    }
    return result;
}

napi_value NapiSEOmaSEService::CreateService(napi_env env, napi_callback_info info)
{
    DebugLog("Enter function NapiSEOmaSEService::CreateService:");
    size_t argc = ARGV_NUM_0;
    napi_value argv[] = {nullptr};
    napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);

    std::unique_ptr<ServiceContext> asyncContext = std::make_unique<ServiceContext>(env);
    asyncContext->service = std::make_shared<SEService>();
    if (asyncContext->service == nullptr) {
        ErrorLog("GetService asyncContext->service nullptr!");
        return NapiSEOmaCommon::CreateUndefined(env);
    }
    return NapiSEOmaCommon::HandleAsyncWork(env, asyncContext.release(), "CreateService",
        NativeCreateService, NativeCreateServiceCallBack);
}

napi_value NapiSEOmaSEService::On(napi_env env, napi_callback_info info)
{
    // js method on("stateChanged",callback)
    DebugLog("Enter function NapiSEOmaSEService::On");
    napi_value thisVar = nullptr;
    size_t argc = ARGV_NUM_2;
    napi_value argv[ARGV_NUM_2] = {nullptr};
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
    NapiSEOmaCommon::CheckArgCountAndThrow(env, argc, ARGV_NUM_2);

    std::string eventType;
    napi_valuetype handler = napi_undefined;
    napi_typeof(env, argv[ARGV_NUM_1], &handler);
    if (!NapiSEOmaCommon::ParseString(env, eventType, argv[ARGV_NUM_0]) || handler != napi_function) {
        ErrorLog("Fail to parse napi args.");
        NapiSEOmaCommon::ThrowParameterError(env);
    }
    int ret = SeServiceEventRegister::GetInstance().Register(env, eventType, argv[ARGV_NUM_1]);
    if (ret != ERR_SUCCESS) {
        ErrorLog("Register error [%{public}d].", ret);
        napi_throw(env,
            NapiSEOmaCommon::GenerateBusinessError(
                env, ret, NapiSEOmaCommon::BuildErrorMessage(ret, "On", EMPTY_STR, EMPTY_STR, EMPTY_STR), ""));
    }
    DebugLog("On, register finish.");
    return NapiSEOmaCommon::CreateUndefined(env);
}

napi_value NapiSEOmaSEService::Off(napi_env env, napi_callback_info info)
{
    // js method off("stateChanged",callback)
    DebugLog("Enter function NapiSEOmaSEService::Off");
    napi_value thisVar = nullptr;
    size_t argc = ARGV_NUM_2;
    napi_value argv[ARGV_NUM_2] = {nullptr};
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
    NapiSEOmaCommon::CheckArgCountAndThrow(env, argc, ARGV_NUM_2);

    std::string eventType;
    if (!NapiSEOmaCommon::ParseString(env, eventType, argv[ARGV_NUM_0])) {
        ErrorLog("Fail to parse napi args.");
        NapiSEOmaCommon::ThrowParameterError(env);
    }

    napi_valuetype handler = napi_undefined;
    napi_typeof(env, argv[ARGV_NUM_1], &handler);
    if (handler == napi_null || handler == napi_undefined || handler != napi_function) {
        argc = ARGV_NUM_1;
        InfoLog("argv[1] is null, undefined or not napi function, handle as no argv[1] input");
    }

    int ret = SeServiceEventRegister::GetInstance().UnRegister(
        env, eventType, argc > ARGV_NUM_1 ? argv[ARGV_NUM_1] : nullptr);
    if (ret != ERR_SUCCESS) {
        ErrorLog("UnRegister error [%{public}d].", ret);
        napi_throw(env,
            NapiSEOmaCommon::GenerateBusinessError(
                env, ret, NapiSEOmaCommon::BuildErrorMessage(ret, "Off", EMPTY_STR, EMPTY_STR, EMPTY_STR), ""));
    }
    DebugLog("Off, unregister finish.");
    return NapiSEOmaCommon::CreateUndefined(env);
}

napi_value NapiSEOmaSEService::GetReaders(napi_env env, napi_callback_info info)
{
    DebugLog("Enter function NapiSEOmaSEService::GetReaders");
    napi_value thisVar = nullptr;
    napi_value argv[] = {nullptr};
    napi_value jsReaderList;
    size_t argc = ARGV_NUM_0;
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
    NapiSEOmaCommon::CheckArgCountAndThrow(env, argc, ARGV_NUM_0);

    // unwrap from thisVar to retrieve the native instance
    NapiSEOmaSEService *napiService = nullptr;
    napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&napiService));
    NAPI_ASSERT(env, status == napi_ok, "failed to get napiService");
    if (napiService == nullptr || napiService->seService == nullptr) {
        ErrorLog("GetService napiService nullptr!");
        return NapiSEOmaCommon::CreateUndefined(env);
    }

    // get readers by calling native method, throw exception when failed
    std::vector<std::shared_ptr<Reader>> readerList;
    int statusCode = napiService->seService->GetReaders(readerList);
    if (statusCode != ErrorCode::SE_SUCCESS || readerList.empty()) {
        ErrorLog("BytesVectorToJS readerList empty, statusCode = %{public}d", statusCode);
        return NapiSEOmaCommon::CreateUndefined(env);
    }
    status = CreateJSReaderList(env, readerList, jsReaderList);
    if (status != napi_ok) {
        ErrorLog("NapiSEOmaSEService::GetReaders CreateJSReaderList failed, status = %{public}d", status);
        return NapiSEOmaCommon::CreateUndefined(env);
    }
    return jsReaderList;
}

napi_value NapiSEOmaSEService::IsConnected(napi_env env, napi_callback_info info)
{
    DebugLog("Enter function NapiSEOmaSEService::IsConnected ");
    napi_value result = nullptr;
    napi_value thisVar = nullptr;
    size_t argc = ARGV_NUM_0;
    napi_value argv[] = {nullptr};
    bool isConnect = false;
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
    DebugLog("IsConnected argc = %{public}d", static_cast<int>(argc));
    NapiSEOmaCommon::CheckArgCountAndThrow(env, argc, ARGV_NUM_0);

    // unwrap from thisVar to retrieve the native instance
    NapiSEOmaSEService *napiService = nullptr;
    napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&napiService));
    NAPI_ASSERT(env, status == napi_ok, "failed to get napiService");
    if (napiService == nullptr || napiService->seService == nullptr) {
        ErrorLog("GetService napiService nullptr!");
        return NapiSEOmaCommon::CreateUndefined(env);
    }

    // obtain the service connection status, throw exception when failed
    int statusCode = napiService->seService->IsConnected(isConnect);
    if (statusCode != ErrorCode::SE_SUCCESS) {
        ErrorLog("IsConnected failed, statusCode = %{public}d", statusCode);
        napi_get_boolean(env, isConnect, &result);
        return NapiSEOmaCommon::CreateUndefined(env);
    }
    napi_get_boolean(env, isConnect, &result);
    DebugLog("SeService::IsConnected: %{public}d", isConnect);
    return result;
}

napi_value NapiSEOmaSEService::Shutdown(napi_env env, napi_callback_info info)
{
    DebugLog("Enter function NapiSEOmaSEService::Shutdown:");
    napi_value thisVar = nullptr;
    size_t argc = ARGV_NUM_0;
    napi_value argv[] = {nullptr};
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
    NapiSEOmaCommon::CheckArgCountAndThrow(env, argc, ARGV_NUM_0);

    // unwrap from thisVar to retrieve the native instance
    NapiSEOmaSEService *napiService = nullptr;
    napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&napiService));
    NAPI_ASSERT(env, status == napi_ok, "failed to get napiService");
    if (napiService == nullptr || napiService->seService == nullptr) {
        ErrorLog("GetService napiService nullptr!");
        return NapiSEOmaCommon::CreateUndefined(env);
    }

    // shutdown service by call native method, throw exception when failed
    int statusCode = napiService->seService->Shutdown();
    if (statusCode != ErrorCode::SE_SUCCESS) {
        ErrorLog("Shutdown failed, statusCode = %{public}d", statusCode);
        return NapiSEOmaCommon::CreateUndefined(env);
    }
    DebugLog("SeService::Shutdown success");
    return NapiSEOmaCommon::CreateUndefined(env);
}

napi_value NapiSEOmaSEService::GetVersion(napi_env env, napi_callback_info info)
{
    DebugLog("Enter function NapiSEOmaSEService::GetVersion:");
    napi_value result = nullptr;
    napi_value thisVar = nullptr;
    size_t argc = ARGV_NUM_0;
    napi_value argv[] = {nullptr};
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
    NapiSEOmaCommon::CheckArgCountAndThrow(env, argc, ARGV_NUM_0);

    // unwrap from thisVar to retrieve the native instance
    NapiSEOmaSEService *napiService = nullptr;
    napi_status status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&napiService));
    NAPI_ASSERT(env, status == napi_ok, "failed to get napiService");
    if (napiService == nullptr || napiService->seService == nullptr) {
        ErrorLog("GetService napiService nullptr!");
        return NapiSEOmaCommon::CreateUndefined(env);
    }

    // get version by call native method, throw exception when failed
    std::string version = "";
    int statusCode = napiService->seService->GetVersion(version);
    if (statusCode != ErrorCode::SE_SUCCESS) {
        ErrorLog("GetVersion failed, statusCode = %{public}d", statusCode);
        return NapiSEOmaCommon::CreateUndefined(env);
    }
    napi_create_string_utf8(env, version.c_str(), NAPI_AUTO_LENGTH, &result);
    DebugLog("SeService::GetVersion:%{public}s", version.c_str());
    return result;
}
SeServiceEventRegister& SeServiceEventRegister::GetInstance()
{
    static SeServiceEventRegister instance;
    return instance;
}
int SeServiceEventRegister::Register(const napi_env& env, const std::string& type, napi_value handler)
{
    DebugLog("Register event = %{public}s", type.c_str());
    if (type != SE_SERVICE_EVENT) {
        ErrorLog("Event type not support!");
        return BUSI_ERR_PARAM_EXCEPTION;
    }
    std::lock_guard<std::mutex> lock(g_mutex);
    g_env = env;
    napi_ref handlerRef = nullptr;
    napi_create_reference(env, handler, DEFAULT_REF_COUNT, &handlerRef);
    std::string uniqueTag = CommonUtils::GetUniqueTag();
    auto iter = g_connectCallbacks.find(uniqueTag);
    if (iter == g_connectCallbacks.end()) {
        g_connectCallbacks[uniqueTag] = std::vector<napi_ref>({handlerRef});
        return ERR_SUCCESS;
    }
    bool hasSameRegObj = false;
    for (auto miter : iter->second) {
        napi_value handlerTemp = nullptr;
        napi_get_reference_value(g_env, miter, &handlerTemp);
        bool isEqual = false;
        napi_strict_equals(g_env, handlerTemp, handler, &isEqual);
        if (isEqual) {
            WarnLog("handler function is same");
            hasSameRegObj = true;
            break;
        }
    }
    if (!hasSameRegObj) {
        g_connectCallbacks[uniqueTag].emplace_back(handlerRef);
    } else {
        napi_delete_reference(env, handlerRef);
    }
    return ERR_SUCCESS;
}

int SeServiceEventRegister::UnRegister(const napi_env& env, const std::string& type, napi_value handler)
{
    DebugLog("UnRegister event = %{public}s", type.c_str());
    if (type != SE_SERVICE_EVENT) {
        ErrorLog("Event type not support!");
        return BUSI_ERR_PARAM_EXCEPTION;
    }
    std::lock_guard<std::mutex> lock(g_mutex);
    std::string uniqueTag = CommonUtils::GetUniqueTag();
    if (handler == nullptr) {
        g_connectCallbacks[uniqueTag].clear();
        return ERR_SUCCESS;
    }
    for (auto iter = g_connectCallbacks[uniqueTag].begin(); iter != g_connectCallbacks[uniqueTag].end(); ++iter) {
        napi_value handlerTemp = nullptr;
        napi_get_reference_value(env, *iter, &handlerTemp);
        bool isEqual = false;
        napi_strict_equals(env, handlerTemp, handler, &isEqual);
        if (isEqual) {
            g_connectCallbacks[uniqueTag].erase(iter);
            break;
        }
    }
    return ERR_SUCCESS;
}
} // namespace KITS
}  // namespace SE
}  // namespace OHOS
